package com.zxy.ziems.collector.modbus;

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.msg.ModbusRequestBuilder;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusRequest;
import com.intelligt.modbus.jlibmodbus.msg.response.*;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.intelligt.modbus.jlibmodbus.utils.ModbusFunctionCode;
import com.zxy.btp.common.util.JsonUtils;
import com.zxy.ziems.collector.parse.ByteResponse;
import com.zxy.ziems.collector.utils.AssertUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * modbus
 *
 * @author 10348359
 * @create 2024-04-12 14:37
 */
@Slf4j
public class ModbusTcpTemplate {
    private ModbusMaster master;

    private String ip;
    private Integer port;

    public ModbusTcpTemplate(String ip, Integer port) {
        this.ip = ip;
        this.port = port;
        this.master = createTcpMaster(ip, port);
    }

    public static ModbusMaster createTcpMaster(String ip, Integer port) {
        ModbusMaster master = null;
        try {
            TcpParameters tcpParameters = new TcpParameters();
            // 设置TCP的ip地址
            InetAddress adress = InetAddress.getByName(ip);

            tcpParameters.setHost(adress);

            // TCP设置长连接
            tcpParameters.setKeepAlive(true);
            // TCP设置端口，这里设置是默认端口502
            tcpParameters.setPort(port);

            // 创建一个主机
            master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
            Modbus.setAutoIncrementTransactionId(true);
            if (!master.isConnected()) {
                master.connect();// 开启连接
            }
        } catch (Exception e) {
            log.error("客户端 :" + ip + ":" + port + "初始化失败" + " exception: " + JsonUtils.toJSONString(e.getMessage()));
            e.printStackTrace();
        }
        return master;
    }

    static Boolean isRead(ModbusFunctionCode functionCode) {
        switch (functionCode) {
            case READ_COILS:
            case READ_DISCRETE_INPUTS:
            case READ_HOLDING_REGISTERS:
            case READ_INPUT_REGISTERS: {
                return true;
            }
            default: {
                return false;
            }
        }
    }

    static Boolean isWrite(ModbusFunctionCode functionCode) {
        switch (functionCode) {
            case WRITE_SINGLE_COIL:
            case WRITE_SINGLE_REGISTER:
            case WRITE_MULTIPLE_COILS:
            case WRITE_MULTIPLE_REGISTERS: {
                return true;
            }
            default: {
                return false;
            }
        }
    }

    /**
     * @param slaveId
     * @param startAddress
     * @param quantity
     * @return
     */
    public ModbusByteResponse read(ModbusFunctionCode function, int slaveId, int startAddress, int quantity, int timeout) throws Exception {
        AssertUtil.isTrue(isRead(function), "功能码错误");
        ModbusByteResponse result = new ModbusByteResponse(function);
        master.setResponseTimeout(timeout);
        switch (function) {
            case READ_COILS: {
                ModbusRequest request = ModbusRequestBuilder.getInstance()
                        .buildReadCoils(slaveId, startAddress, quantity);
                ReadCoilsResponse response = (ReadCoilsResponse) master.processRequest(request);
                result.setBytes(response.getBytes());
            }
            break;
            case READ_DISCRETE_INPUTS: {
                ModbusRequest request = ModbusRequestBuilder.getInstance()
                        .buildReadDiscreteInputs(slaveId, startAddress, quantity);
                ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.processRequest(request);
                result.setBytes(response.getBytes());
            }
            break;
            case READ_HOLDING_REGISTERS: {
                ModbusRequest request = ModbusRequestBuilder.getInstance()
                        .buildReadHoldingRegisters(slaveId, startAddress, quantity);
                ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.processRequest(request);
                result.setBytes(response.getBytes());
            }
            break;
            default: {
                AssertUtil.isTrue(false, "功能码没有实现");
            }
        }
        return result;
    }

    public ModbusByteResponse readWithReconncet(ModbusFunctionCode function, int slaveId, int startAddress, int quantity, int timeout) throws ModbusIOException {
        ModbusByteResponse result = null;
        try {
            result = read(function, slaveId, startAddress, quantity, timeout);
        } catch (Exception e) {
            log.error("客户端 :" + ip + ":" + port + " slaveId: " + slaveId + "读取数据异常" + " exception: " + JsonUtils.toJSONString(e.getMessage()));
            e.printStackTrace();
            try {
                log.info("客户端 :" + ip + ":" + port + " 尝试重连");
                master.disconnect();
                if (!master.isConnected()) {
                    master.connect();
                }
                result = read(function, slaveId, startAddress, quantity, timeout);
            } catch (Exception ex) {
                log.error("客户端 :" + ip + ":" + port + "重连失败" + " exception: " + JsonUtils.toJSONString(ex.getMessage()));
                ex.printStackTrace();
            }
        }
        return result;
    }

    public void write(ModbusFunctionCode function, int slaveId, int startAddress, int register, int timeout) {
        AssertUtil.isTrue(isWrite(function), "功能码错误");
        try {
            master.setResponseTimeout(timeout);
            switch (function) {
                case WRITE_SINGLE_COIL: {
                    ModbusRequest request = ModbusRequestBuilder.getInstance()
                            .buildWriteSingleCoil(slaveId, startAddress, register != 0);
                    WriteSingleCoilResponse response = (WriteSingleCoilResponse) master.processRequest(request);
                }
                break;
                case WRITE_SINGLE_REGISTER: {
                    ModbusRequest request = ModbusRequestBuilder.getInstance()
                            .buildWriteSingleRegister(slaveId, startAddress, register);
                    WriteSingleRegisterResponse response = (WriteSingleRegisterResponse) master.processRequest(request);
                }
                break;
                default: {
                    AssertUtil.isTrue(false, "功能码没有实现");
                }
            }


        } catch (ModbusProtocolException e) {
            e.printStackTrace();
        } catch (ModbusNumberException e) {
            e.printStackTrace();
        } catch (ModbusIOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param slaveId
     * @param startAddress
     * @param quantity
     * @return
     */
    public ByteResponse readHoldingRegister(int slaveId, int startAddress, int quantity, int timeout) throws ModbusIOException {
        ByteResponse result = new ByteResponse();
        try {
            master.setResponseTimeout(timeout);
            ModbusRequest request = ModbusRequestBuilder.getInstance()
                    .buildReadHoldingRegisters(slaveId, startAddress, quantity);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.processRequest(request);
            result.setBytes(response.getBytes());
        } catch (Exception e) {
            try {
                log.info("客户端 :" + ip + ":" + port + " 尝试重连");
                master.disconnect();
                if (!master.isConnected()) {
                    master.connect();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }

    public ModbusTcpTemplate writeHoldingRegister(int slaveId, int startAddress, int register, int timeout) {
        ByteResponse result = new ByteResponse();
        try {
            master.setResponseTimeout(timeout);
            ModbusRequest request = ModbusRequestBuilder.getInstance().buildWriteSingleRegister(slaveId, startAddress, register);
            WriteSingleRegisterResponse response = (WriteSingleRegisterResponse) master.processRequest(request);

        } catch (ModbusProtocolException e) {
            e.printStackTrace();
        } catch (ModbusNumberException e) {
            e.printStackTrace();
        } catch (ModbusIOException e) {
            e.printStackTrace();
        }
        return this;
    }

    public void disconnect() {
        try {
            master.disconnect();
            log.info("[采集器]关闭modbus tcp成功");
        } catch (Exception e) {
            log.error("[采集器]关闭modbus tcp 失败");
        }
    }
}
